package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.common.constants.message.ArticleForEsConstants;
import com.heima.common.constants.message.NewsAutoScanConstants;
import com.heima.common.constants.message.WmNewsMessageConstants;
import com.heima.common.constants.wemedia.WemediaConstants;
import com.heima.common.exception.CustException;
import com.heima.common.exception.CustomException;
import com.heima.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.media.dto.NewsAuthDto;
import com.heima.model.media.dto.WmNewsDto;
import com.heima.model.media.dto.WmNewsPageReqDto;
import com.heima.model.media.pojo.WmMaterial;
import com.heima.model.media.pojo.WmNews;
import com.heima.model.media.pojo.WmNewsMaterial;
import com.heima.model.media.pojo.WmUser;
import com.heima.model.media.vo.WmNewsVo;
import com.heima.utils.threadlocal.WmThreadLocalUtils;
import com.heima.wemedia.mapper.WmMaterialMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmNewsMaterialMapper;
import com.heima.wemedia.mapper.WmUserMapper;
import com.heima.wemedia.service.WmNewsService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.kafka.common.protocol.types.Field;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;
@Slf4j
@Service
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {
    @Value("${file.oss.web-site}")
    String webSite;

    @Override
    public ResponseResult list(WmNewsPageReqDto dto) {
        //1.检验参数
        dto.checkParam();
        //2.检验用户是否登录
        WmUser user = WmThreadLocalUtils.getUser();
        if (null == user) {
            CustException.cust(AppHttpCodeEnum.NEED_LOGIN);
        }
        //3.构建查询条件
        IPage<WmNews> pageReq = new Page<>(dto.getPage(), dto.getSize());

        //3.1 分页查询条件
        LambdaQueryWrapper<WmNews> queryWrapper = Wrappers.<WmNews>lambdaQuery();
        //3.2关键字模糊查询
        if (StringUtils.isNotBlank(dto.getKeyword())) {
            queryWrapper.like(WmNews::getTitle, dto.getKeyword());
        }
        //3.3频道id
        if (dto.getChannelId() != null) {
            queryWrapper.eq(WmNews::getChannelId, dto.getChannelId());
        }
        //3.3文章状态
        if (dto.getStatus() != null) {
            queryWrapper.eq(WmNews::getStatus, dto.getStatus());
        }
        //3.4起止时间
        if (dto.getBeginPubDate() != null && dto.getEndPubDate() != null) {
            queryWrapper.between(WmNews::getPublishTime, dto.getBeginPubDate(), dto.getEndPubDate());
        }
        //3.5按照用户id查询
        queryWrapper.eq(WmNews::getUserId, user.getId());
        //3.6按照发布时间降序
        queryWrapper.orderByDesc(WmNews::getPublishTime);
        //4.分页查询
        IPage<WmNews> result = this.page(pageReq, queryWrapper);  //todo
        //5.响应
        ResponseResult responseResult = new PageResponseResult(dto.getPage(), dto.getSize(), result.getTotal());
        responseResult.setData(result.getRecords());
        responseResult.setHost(webSite);
        return responseResult;
    }

    @Override
    public ResponseResult submitNews(WmNewsDto dto) {
        //1.检查参数
        //1.1检查文章内容
        if (StringUtils.isBlank(dto.getContent())) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID, "文章内容不能为空");
        }
        //1.2检查文章状态
        Short isSubmit = dto.getStatus();
        if (!isSubmit.equals(WemediaConstants.WM_NEWS_DRAFT_STATUS) && !isSubmit.equals(WemediaConstants.WM_NEWS_SUMMIT_STATUS)) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID, "状态参数有误");
        }
        //1.3是否登录
        WmUser user = WmThreadLocalUtils.getUser();
        if (user == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }
        //1.4拷贝
        WmNews wmNews = new WmNews();

        BeanUtils.copyProperties(dto, wmNews);
        // 1.6 如果type为自动生成 暂时将type设置为null
        if (dto.getType().equals(WemediaConstants.WM_NEWS_TYPE_AUTO)) {
            wmNews.setType(null); // 这个字段数据库中用的是无符号字段，无法存储负数  会报错
        }
        // 1.7 如果封装images集合不为null  拼接成字符串 并替换前缀
        List<String> images = dto.getImages();
        if (images != null && images.size() > 0) {
            wmNews.setImages(imagesListToStr(images));
        }
        wmNews.setUserId(user.getId());
        // 2. 保存或修改 WmNews对象
        saveOrUpdateWmNews(wmNews);
        //3. 保存文章和素材的关联关系
        // 3.1 抽取文章内容中涉及的图片
        List<String> contentImages = parseContentImages(dto.getContent());
        // 3.2 保存 文章内容所引用的图片素材的关联关系
        if (contentImages.size() > 0 && contentImages != null && isSubmit.equals(WemediaConstants.WM_NEWS_SUMMIT_STATUS)) {
            saveRelativeInfoForContent(contentImages, wmNews);
        }
        //3.3 保存 文章封面所引用的图片素材的关联关系
        if (isSubmit.equals(WemediaConstants.WM_NEWS_SUMMIT_STATUS)) {
            saveRelativeInfoForCover(contentImages, wmNews, dto);
            //kafka发消息
            kafkaTemplate.send(NewsAutoScanConstants.WM_NEWS_AUTO_SCAN_TOPIC,String.valueOf(wmNews.getId()));
            log.info("发表文章成功，并通过kafka通知admin端审核  ==> {}",wmNews.getId());
        }
        return ResponseResult.okResult();
    }
    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;

    /**
     * 根据id查询文章内容
     *
     * @param id
     * @return
     */

    @Override
    public ResponseResult findWmNewsById(Integer id) {
        if (null == id) {
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID, "id为null");
        }
        WmNews wmNews = getById(id);
        if (wmNews == null) {
            throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST, "文章内容为空");
        }
        ResponseResult result = ResponseResult.okResult(wmNews);
        result.setHost(webSite);
        System.out.println(result.toString());
        return result;
    }

    @Override
    public ResponseResult delNews(Integer id) {
        //1. 检查参数
        if (null == id) {
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID, "id不存在");
        }
        WmUser user = WmThreadLocalUtils.getUser();
        if (null == user) {
            throw new CustomException(AppHttpCodeEnum.NEED_LOGIN);
        }
        //2.检查文章
        WmNews wmNews = getById(id);
        if (wmNews == null) {
            throw new CustomException(AppHttpCodeEnum.AP_USER_DATA_NOT_EXIST, "文章不存在");
        }
        //3.检查文章状态
        if (WemediaConstants.WM_NEWS_PUBLISH_STATUS.equals(wmNews.getStatus()) && WemediaConstants.WM_NEWS_ENABLE_UP.equals(wmNews.getEnable())) {
            throw new CustomException(AppHttpCodeEnum.DATA_NOT_ALLOW);
        }
        //  4.判断是否登录用户上传的
        if(!wmNews.getUserId().equals(user.getId())){
            throw new CustomException(AppHttpCodeEnum.NO_OPERATOR_AUTH,"不是本人 上传的，无权限操作");
        }
        //5.删除文章与素材的关联关联关系
        wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery().eq(WmNewsMaterial::getNewsId,id));

        //6.删除文章数据
        removeById(id);
        return ResponseResult.okResult();
    }

    @Override
    public ResponseResult downOrUp(WmNewsDto dto) {
        // 1. 检查参数  (id    enable = 0 或 1)
        if(dto.getId()==null){
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID,"文章id不能为空");
        }
        if(!WemediaConstants.WM_NEWS_ENABLE_UP.equals(dto.getEnable())
                &&!WemediaConstants.WM_NEWS_ENABLE_DOWN.equals(dto.getEnable())){
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID,"文章上下状态错误");
        }
        // 2. 根据id查询文章
        WmNews wmNews = getById(dto.getId());
        if(wmNews==null){
            throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST,"文章不存在");
        }
        // 3. 检查status 是否为9
        if(!wmNews.getStatus().equals(WemediaConstants.WM_NEWS_PUBLISH_STATUS)){
            throw new CustomException(AppHttpCodeEnum.DATA_NOT_ALLOW,"只有发布状态的文章才可以上下架");
        }
        // 4. 修改enable 字符
        LambdaUpdateWrapper<WmNews> updateWrapper = Wrappers.<WmNews>lambdaUpdate();
        updateWrapper.set(WmNews::getEnable,dto.getEnable());
        if(dto.getEnable().equals(WemediaConstants.WM_NEWS_ENABLE_DOWN)){
            updateWrapper.set(WmNews::getStatus,WemediaConstants.WM_NEWS_SUMMIT_STATUS);
        }
        updateWrapper.eq(WmNews::getId,dto.getId()).eq(WmNews::getUserId,WmThreadLocalUtils.getUser().getId());
        update(updateWrapper);
        //kafka发消息
        if (wmNews.getArticleId() !=null) {
            Map<String, Object> map = new HashMap<>();
            map.put("enable",dto.getEnable());
            map.put("articleId",wmNews.getArticleId());
            kafkaTemplate.send(WmNewsMessageConstants.WM_NEWS_UP_OR_DOWN_TOPIC, JSON.toJSONString(map));
        }
        //是否上架  0 下架  1 上架
        // ==============新加代码==============================================
        Map map = new HashMap();
        map.put("articleId",wmNews.getArticleId());
        if (dto.getEnable().intValue()==0){
            kafkaTemplate.send(ArticleForEsConstants.ARTICLE_ELASTICSEARCH_DELETE,JSON.toJSONString(map));
        }else {
            kafkaTemplate.send(ArticleForEsConstants.ARTICLE_ELASTICSEARCH_ADD,JSON.toJSONString(map));
        }
        return ResponseResult.okResult();
    }
    /**
     * 查询需要发布的文章id列表
     * @return
     */
    @Override
    public List<Integer> findRelease() {
        List<WmNews> wmNewsList = this.list(Wrappers.<WmNews>lambdaQuery()
                .lt(WmNews::getPublishTime, new Date())  //发布时间小于等于当前时间
                .in(WmNews::getStatus, 4, 8)  //发布状态为4或8
                .select(WmNews::getId));    //只取id字段的WmNews集合
        return wmNewsList.stream().map(WmNews::getId).collect(Collectors.toList());
    }

    /**
     * 查询文章列表
     * @param dto
     * @return
     */
    @Autowired
    private WmNewsMapper wmNewsMapper;
    @Override
    public ResponseResult findList(NewsAuthDto dto) {
        //1.检查参数
        dto.checkParam();
        //设置当前页
        Integer currentPage = dto.getPage();
        //设置起始页
        dto.setPage((dto.getPage()-1)*dto.getSize());
        //根据搜索内容模糊查询
        if (StringUtils.isNotBlank(dto.getTitle())) {
            dto.setTitle("%"+dto.getTitle()+"%");
        }
        //2.分页查询
        List<WmNewsVo> wmNewsVoList = this.wmNewsMapper.findListAndPage(dto);
        //统计数据条数
        long count = this.wmNewsMapper.findListCount(dto);  //TODO
        //3.返回结果
        ResponseResult responseResult = new PageResponseResult(currentPage, dto.getSize(), count);
        responseResult.setData(wmNewsVoList);
        responseResult.setHost(webSite);
        return responseResult;
    }
    @Autowired
    private WmUserMapper wmUserMapper;
    @Override
    public ResponseResult findWmNewsVo(Integer id) {
        //校验参数
        if (null ==id) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"id为null");
        }
        //查询文章信息
        WmNews wmNews = this.getById(id);
        if (null == wmNews) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"该文章不存在");
        }
        //查询作者信息
        Integer userId = wmNews.getUserId();
        WmUser wmUser = this.wmUserMapper.selectById(userId);
        if (null == wmUser) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"作者信息不存在");
        }
        //封装vo,响应结果
        WmNewsVo wmNewsVo = new WmNewsVo();
        BeanUtils.copyProperties(wmNews,wmNewsVo);
        wmNewsVo.setAuthorName(wmUser.getName());
        ResponseResult result = ResponseResult.okResult(wmNewsVo);
        result.setHost(webSite);
        return result;
    }

    @Override
    public ResponseResult updateStatus(Short status, NewsAuthDto dto) {
        //1.校验参数
        if (null ==dto) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"参数为空");
        }
        //2.校验文章信息
        WmNews wmNews = this.getById(dto.getId());
        if (wmNews == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"文章信息不存在");
        }
        //3.获取状态
        wmNews.setStatus(status);
        //4.设置理由信息
        if (StringUtils.isNotBlank(dto.getMsg())) {
            wmNews.setReason(dto.getMsg());
        }
        //4.更新
        this.updateById(wmNews);
        //5.响应结果
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    /**
     * 保存 封面 和 素材的关联关系
     * 如果封面是自动生成 需要按照规则 从内容图片中获取封面
     *
     * @param contentImages 内容中图片列表
     * @param wmNews        要保存的文章信息
     * @param dto           入参实体类
     */
    private void saveRelativeInfoForCover(List<String> contentImages, WmNews wmNews, WmNewsDto dto) {
        List<String> imagesList = dto.getImages();
        // 判断是否自动生成封面  //如果 type类型为-1 需要从文章内容图片中生成封面
        if (WemediaConstants.WM_NEWS_TYPE_AUTO.equals(dto.getType())) {
            if (contentImages != null && contentImages.size() > 0) {
                int size = contentImages.size();
                // 1. 如果图片数量大于2  type=3 多图  取内容中前3张图片作为封面
                if (size > 2) {
                    imagesList = contentImages.stream().limit(3).collect(Collectors.toList());
                    wmNews.setType(WemediaConstants.WM_NEWS_MANY_IMAGE);
                } else if (size > 0 && size <= 2) {
                    // 2. 如果图片数量大于0  小于2  type=1 单图  取内容中前1张图片作为封面
                    imagesList = contentImages.stream().limit(1).collect(Collectors.toList());
                    wmNews.setType(WemediaConstants.WM_NEWS_SINGLE_IMAGE);
                }
            } else {
                // 3. 其它情况 无图模式   type=0
                wmNews.setType(WemediaConstants.WM_NEWS_NONE_IMAGE);
            }
            // 将封面图片集合 转为逗号拼接的字符串
            wmNews.setImages(imagesListToStr(imagesList));  // 图片字符串
            updateById(wmNews);
        }
        if (imagesList != null && imagesList.size() > 0) {
            // 确保替换掉路径前缀
            imagesList = imagesList.stream().map(url -> url.replaceAll(webSite, "")).collect(Collectors.toList());
            // 保存 封面 与 素材的关联关系
            saveRelativeInfo(imagesList, wmNews.getId(), WemediaConstants.WM_IMAGE_REFERENCE);
        }


    }

    /**
     * 保存 内容中引用素材的关联关系
     *
     * @param contentImages
     * @param wmNews
     */
    private void saveRelativeInfoForContent(List<String> contentImages, WmNews wmNews) {
        saveRelativeInfo(contentImages, wmNews.getId(), WemediaConstants.WM_CONTENT_REFERENCE);

    }

    @Autowired
    private WmMaterialMapper wmMaterialMapper;

    /**
     * 保存 文章 和 素材的 关联关系
     *
     * @param materialUrls 素材的图片路径集合
     * @param newsId       文章的id
     * @param type         引用的类型   0 内容引用    1 封面引用
     */
    private void saveRelativeInfo(List<String> materialUrls, Integer newsId, Short type) {
        // 根据素材路径集合  及 登录用户userId 查询素材集合
        List<WmMaterial> wmMaterials = wmMaterialMapper.selectList(Wrappers.<WmMaterial>lambdaQuery().
                eq(WmMaterial::getUserId, WmThreadLocalUtils.getUser().getId())
                .in(WmMaterial::getUrl, materialUrls));
        if (wmMaterials == null || wmMaterials.size() == 0) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID, "素材不存在");
        }
        // 将素材列表转为map集合   key: 图片url路径       value: 素材的id
        Map<String, Integer> urlAndIdMap = wmMaterials.stream().collect(Collectors.toMap(WmMaterial::getUrl, WmMaterial::getId));//todo 为什么用map
        // 查看引用素材路径列表 ，在map中是否引用，如果有没有引用的 抛出异常
        List<Integer> materialIds = new ArrayList<>();
        materialUrls.forEach(url -> {
            if (!urlAndIdMap.containsKey(url)) {
                throw new CustomException(AppHttpCodeEnum.PARAM_INVALID, "引用的素材不存在");
            }
            materialIds.add(urlAndIdMap.get(url));
        });
        // 收集所有素材ID组成一个集合
        wmNewsMaterialMapper.saveRelations(materialIds, newsId, type);


    }

    /**
     * 将文章内容中的图片抽取出来
     *
     * @param content
     * @return
     */
    private List<String> parseContentImages(String content) {
        // 使用fastjson 将文章内容转为list集合 集合中的每一个元素 都是一个map
        // map :    type ( text  或  image)      value
        List<Map> contentImages = JSONArray.parseArray(content, Map.class);
        List<String> contentImagesList = contentImages.stream()
                .filter(map -> WemediaConstants.WM_NEWS_TYPE_IMAGE.equals(map.get("type"))) //过滤保留type=image的内容
                .map(map -> (String) map.get("value"))   //映射,获取type=image的map
                .map(url -> url.replaceAll(webSite, "")) //将url中website前缀去掉
                .collect(Collectors.toList());//收集到一个集合中
        return contentImagesList;
    }

    @Autowired
    private WmNewsMaterialMapper wmNewsMaterialMapper;

    private void saveOrUpdateWmNews(WmNews wmNews) {
        // 1. 补全wmNews 信息   创建时间   提交时间   登录用户  上下架
        wmNews.setCreatedTime(new Date());
        wmNews.setSubmitedTime(new Date());
        wmNews.setEnable(WemediaConstants.WM_NEWS_ENABLE_UP);
        // 2. 判断id的值是否为空
        if (wmNews.getId() == null) {
            // 2.1 如果id为空   新增该wmNews
            save(wmNews);
        } else {
            // 2.2 如果id不为空   先删除之前的关联关系   然后修改wmNews
            wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery().eq(WmNewsMaterial::getNewsId, wmNews.getId()));
            updateById(wmNews);// 修改文章
        }

    }

    /**
     * 将图片列表集合  用逗号拼接成 一个字符串  并且替换前缀路径
     *
     * @param images
     * @return
     */
    private String imagesListToStr(List<String> images) {
        return images.stream().
                map(url -> url.replace(webSite, ""))
                .collect(Collectors.joining(","));
    }
}
